package com.liu.stockmarket.ui.statistics

import androidx.lifecycle.*
import com.github.mikephil.charting.data.Entry
import com.liu.stockmarket.VO.StatisticsVO
import com.liu.stockmarket.model.HoldingStockProfit
import com.liu.stockmarket.model.Stock
import com.liu.stockmarket.repository.database.ProfitRepository
import com.liu.stockmarket.repository.database.StockRepository
import com.liu.stockmarket.repository.database.TransactionRecordRepository
import com.liu.stockmarket.repository.database.entity.ProfitEntity
import com.liu.stockmarket.repository.database.entity.StockEntity
import com.liu.stockmarket.repository.database.entity.TransactionRecordEntity
import com.liu.stockmarket.repository.network.EasyMoneyRepository
import com.liu.stockmarket.ui.statistics.adapter.HistoryAdapter
import com.liu.stockmarket.ui.statistics.adapter.StatisticsAdapter
import com.liu.stockmarket.ui.stock.HoldingStockAdapter
import com.liu.stockmarket.utils.*
import kotlinx.coroutines.*
import java.math.BigDecimal
import java.time.*

class StatisticsViewModel(
    private val transactionRecordRepository: TransactionRecordRepository,
    private val stockRepository: StockRepository,
    private val profitRepository: ProfitRepository,
    private val easyMoneyRepository: EasyMoneyRepository
) : ViewModel() {

    private val _statisticsData = MutableLiveData<StatisticsVO>()

    val statisticsData: LiveData<StatisticsVO> = _statisticsData


    private val _profitData = MutableLiveData<Map<Stock.StockType, List<Entry>>>()

    val profitData: LiveData<Map<Stock.StockType, List<Entry>>> = _profitData


    /**
     * 盈亏记录
     */
    private val _allProfits = MutableLiveData<List<ProfitEntity>>()

    val allProfits: LiveData<List<ProfitEntity>> = _allProfits


    private var stock: HoldingStockProfit? = null
    private var fund: HoldingStockProfit? = null

    private lateinit var todayProfits: List<ProfitEntity>



    @ExperimentalCoroutinesApi
    fun statistics() {
        viewModelScope.launch(Dispatchers.IO) {
            val stockAll = stockRepository.queryAllBuy()
            val transactionRecordAll = transactionRecordRepository.queryAll()
            val playDay = playDay(transactionRecordAll)

            var start = System.currentTimeMillis()

            val stockAsync = viewModelScope.async(Dispatchers.IO) {
                stock(stockAll.filter {
                    it.stockType != Stock.StockType.FUND
                })
            }


            val fundAsync = viewModelScope.async(Dispatchers.IO) {
                fund(stockAll.filter {
                    it.stockType == Stock.StockType.FUND
                })
            }

            // 并发请求，减少耗时
            stock = stockAsync.await()

            fund = fundAsync.await()

            println("请求耗时：${System.currentTimeMillis()-start}")
            start = System.currentTimeMillis()


            val allStock = getAllStock()

            val totalMoney = allStock.sumOf {
                it.holdingMoney
            }

            todayProfits = parseProfit(allStock)


            val historys = parseHistory(allStock, transactionRecordAll)


            val statistics = parseStatistics(todayProfits, transactionRecordAll)

            _statisticsData.postValue(
                StatisticsVO(
                    playDay,
                    totalMoney,
                    statistics,
                    historys,
                    allStock.filter {
                        it.allProfit > BigDecimal.ZERO
                    }.count(),
                    allStock.filter {
                        it.allProfit == BigDecimal.ZERO
                    }.count(),
                    allStock.filter {
                        it.allProfit < BigDecimal.ZERO
                    }.count()
                )
            )
            println("处理耗时：${System.currentTimeMillis()-start}")
        }
    }

    private fun parseProfit(allStock: List<HoldingStockAdapter.Item>): List<ProfitEntity> {
        val profits = allStock.groupBy {
            it.stockType
        }.map { e ->
            if (e.value.isNullOrEmpty()) {
                ProfitEntity(
                    id = null,
                    stockType = e.key,
                    holdingMoney = BigDecimal.ZERO,
                    profit = BigDecimal.ZERO,
                    percent = BigDecimal.ZERO,
                    todayProfit = BigDecimal.ZERO,
                    todayPercent = BigDecimal.ZERO
                )

            } else {
                val holdingMoney = e.value.sumOf {
                    it.holdingMoney
                }

                val totalProfit = e.value.sumOf {
                    it.allProfit
                }

                val totalProfitPercent = if (totalProfit.setScale(5,BigDecimal.ROUND_CEILING)
                    == BigDecimal.ZERO.setScale(5,BigDecimal.ROUND_CEILING)) {
                    BigDecimal.ZERO
                } else {
                    (totalProfit / (holdingMoney - totalProfit)) * BigDecimal("100").setScale(5,BigDecimal.ROUND_CEILING)
                }


                val todayProfit = e.value.sumOf {
                    it.todayProfit
                }

                val todayProfitPercent = if (todayProfit.setScale(5,BigDecimal.ROUND_CEILING)
                    == BigDecimal.ZERO.setScale(5,BigDecimal.ROUND_CEILING)) {
                    BigDecimal.ZERO
                } else {
                    (todayProfit / (holdingMoney - todayProfit)) * BigDecimal("100").setScale(5,BigDecimal.ROUND_CEILING)
                }

                ProfitEntity(
                    id = null,
                    stockType = e.key,
                    holdingMoney = holdingMoney,
                    percent = totalProfitPercent,
                    profit = totalProfit,
                    todayProfit = todayProfit,
                    todayPercent = todayProfitPercent
                )
            }
        }.toMutableList()

        profits.add(getAllProfitEntity(allStock = allStock))
        return profits.toList()
    }


    fun saveTodayProfit(): Boolean {

        viewModelScope.launch {
            if (todayProfits.isNotEmpty()) {
                profitRepository.save(todayProfits, LocalDate.now())
            }

        }
        return true

    }


    /**
     * @param type 0：总盈亏金额，1：总收益率，2：日收益率，3：持有金额
     */
    fun profitLineChartData(type: Int) {
        viewModelScope.launch(Dispatchers.IO) {

            val profits = profitRepository.queryAll()
            _allProfits.postValue(profits)

            val profitData = when (type) {
                0 -> abc(profits) {
                    it.profit.toFloat()
                }
                1 -> abc(profits) {
                    it.percent.toFloat()
                }
                2 -> abc(profits) {
                    it.todayPercent.toFloat()
                }
                3 -> abc(profits) {
                    it.holdingMoney.toFloat()
                }
                else -> mutableMapOf()
            }


            _profitData.postValue(profitData)

        }
    }

    private fun abc(
        thisProfits: List<ProfitEntity>,
        y: (p: ProfitEntity) -> Float
    ): MutableMap<Stock.StockType, List<Entry>> {
        val profitData = mutableMapOf<Stock.StockType, List<Entry>>()
        thisProfits.groupBy {
            it.stockType
        }.forEach {
            val entryList = it.value
                .sortedBy { pe ->
                    pe.date
                }.map { v ->
                    val x = LocalDateTime.of(v.date, LocalTime.of(9, 0, 0)).toEpochSecond(
                        ZoneOffset.UTC
                    )
                    Entry(x.toFloat(), y.invoke(v), v)
                }
            profitData[it.key] = entryList
        }
        return profitData
    }


    /**
     * 获得每个 可转债、股票、基金 的收益数据
     */
    private fun getAllStock(): List<HoldingStockAdapter.Item> {
        return merge(stock, fund).flatMap {

            return@flatMap it.list

        }.toList()
    }

    /**
     * 获合并可转债、股票、基金 的收益数据
     */
    private fun merge(
        stock: HoldingStockProfit?,
        fund: HoldingStockProfit?
    ): List<HoldingStockProfit> {
        val list = mutableListOf<HoldingStockProfit>()
        if (stock != null) {
            list.add(stock)
        }
        if (fund != null) {
            list.add(fund)
        }
        return list
    }


    private fun getAllProfitEntity(allStock: List<HoldingStockAdapter.Item>): ProfitEntity {
        // 今日总盈亏
        val todayTotalProfit = allStock.sumOf {
            it.todayProfit
        }
        // 持有总盈亏
        val totalProfit = allStock.sumOf {
            it.allProfit
        }

        // 持有总金额
        val totalHoldingMoney = allStock.sumOf {
            it.holdingMoney
        }
        // 今日收益率
        val todayTotalProfitPercent = if (todayTotalProfit.setScale(
                5,
                BigDecimal.ROUND_HALF_UP
            ) == BigDecimal.ZERO.setScale(5, BigDecimal.ROUND_HALF_UP)
        ) {
            BigDecimal.ZERO
        } else {
            (todayTotalProfit / (totalHoldingMoney - todayTotalProfit)) * BigDecimal("100")
                .setScale(5, BigDecimal.ROUND_HALF_UP)
        }
        // 总收益率
        val totalProfitPercent =
            if (totalProfit.setScale(5, BigDecimal.ROUND_HALF_UP) == BigDecimal.ZERO.setScale(
                    5,
                    BigDecimal.ROUND_HALF_UP
                )
            ) {
                BigDecimal.ZERO
            } else {
                (totalProfit / (totalHoldingMoney - totalProfit)) * BigDecimal("100").setScale(
                    5,
                    BigDecimal.ROUND_HALF_UP
                )
            }
        return ProfitEntity(
            id = null,
            stockType = Stock.StockType.ALL,
            holdingMoney = totalHoldingMoney,
            profit = totalProfit,
            percent = totalProfitPercent,
            todayProfit = todayTotalProfit,
            todayPercent = todayTotalProfitPercent
        )
    }


    private fun parseHistory(
        stocks: List<HoldingStockAdapter.Item>,
        transactionRecordAll: List<TransactionRecordEntity>
    ): List<HistoryAdapter.Item> {
        return stocks.map {
            // 该股票的交易记录
            val stockTransactions = transactionRecordAll.filter { record ->
                record.stockId == it.stockId
            }

            // 该股票第一次交易时间
            val firstTransactionDate = stockTransactions.minByOrNull { record ->
                record.transactionDate
            }?.transactionDate ?: LocalDate.now()

            val holdingDay = Duration.between(
                LocalDateTime.of(firstTransactionDate, LocalTime.of(0, 0, 1)),
                LocalDateTime.now()
            ).toDays()


            val handlingFee = stockTransactions.sumOf { record ->
                record.handlingFee
            }



            HistoryAdapter.Item(
                stockId = it.stockId,
                name = it.stockName,
                code = it.stockCode,
                holdingDay = holdingDay,
                totalProfit = it.allProfit,
                todayProfit = it.todayProfit,
                type = it.stockType,
                transactionCount = stockTransactions.count(),
                handlingFee = handlingFee
            )
        }.sortedByDescending {
            it.totalProfit
        }
    }

    private fun BigDecimal?.getValue() = this ?: BigDecimal.ZERO


    private fun parseStatistics(
        profits: List<ProfitEntity>,
        transactionRecordAll: List<TransactionRecordEntity>
    ): List<StatisticsAdapter.Item> {

        val all = profits.filter { it.stockType == Stock.StockType.ALL }.getOrNull(0)
        val stock = profits.filter { it.stockType == Stock.StockType.STOCK }.getOrNull(0)
        val fund = profits.filter { it.stockType == Stock.StockType.FUND }.getOrNull(0)
        val bond = profits.filter { it.stockType == Stock.StockType.BOND }.getOrNull(0)
        val handlingFee = transactionRecordAll.sumOf {
            it.handlingFee
        }

        return listOf(
            StatisticsAdapter.Item("今日盈亏", all?.todayProfit.getValue(), all?.todayPercent.getValue()),
            StatisticsAdapter.Item(
                "总盈亏",
                all?.profit.getValue(), all?.percent.getValue()
            ),

            StatisticsAdapter.Item(
                "股票盈亏",
                stock?.profit.getValue(), stock?.percent.getValue()
            ),
            StatisticsAdapter.Item(
                "可转债盈亏",
                bond?.profit.getValue(), bond?.percent.getValue()
            ),
            StatisticsAdapter.Item(
                "基金盈亏",
                fund?.profit.getValue(), fund?.percent.getValue()
            ),
            StatisticsAdapter.Item("总手续费", -handlingFee),
            StatisticsAdapter.Item("总交易次数", BigDecimal(transactionRecordAll.size), null, false),

            )
    }


    /**
     * 韭菜时间
     */
    private fun playDay(transactionRecordAll: List<TransactionRecordEntity>): String {
        val firstTransactionDate = transactionRecordAll.minByOrNull {
            it.transactionDate
        }?.transactionDate

        if (firstTransactionDate != null) {
            return firstTransactionDate.timeInterval(LocalDate.now())

        }
        return "0天"
    }


    /**
     * 股票的交易信息
     */
    private suspend fun stock(stocks: List<StockEntity>): HoldingStockProfit? {
        return stockProfit(stocks)
    }


    private suspend fun stockProfit(stocks: List<StockEntity>): HoldingStockProfit? {
        if (stocks.isNullOrEmpty()) {
            return null
        }
        return StockTransactionUtil.stock(
            easyMoneyRepository,
            transactionRecordRepository,
            stocks
        ).data
    }

    /**
     * 基金的交易信息
     */
    private suspend fun fund(funds: List<StockEntity>): HoldingStockProfit? {
        if (funds.isNullOrEmpty()) {
            return null
        }
        return StockTransactionUtil.fund(
            easyMoneyRepository,
            transactionRecordRepository,
            funds
        ).data

    }


    class StatisticsViewModelFactory(
        private val transactionRecordRepository: TransactionRecordRepository,
        private val stockRepository: StockRepository,
        private val profitRepository: ProfitRepository,
        private val easyMoneyRepository: EasyMoneyRepository
    ) :
        ViewModelProvider.Factory {
        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
            @Suppress("UNCHECKED_CAST")
            return StatisticsViewModel(
                transactionRecordRepository,
                stockRepository,
                profitRepository,
                easyMoneyRepository
            ) as T
        }

    }
}